# -*- coding: utf-8 -*-
"""
  @Time    : 2023/3/2 8:57
  @Author  : 
  @email   : 
  @File    : containers.py
"""

from flask import Blueprint, request, jsonify, current_app
from lib.docker_manage import DockerEngine
from lib.tools import (
    dict_merge,
    get_name_by_ip,
    utc_to_beijing,
    byte_humanized_display,
    check_endpoint_status,
    res_403,
    res_404,
    res_500,
    SerializeData)
import re
import os

# 定义蓝图，路由分组
containers_bp = Blueprint('containers_bp', __name__, url_prefix='/containers')


@containers_bp.route('/info/<model>', methods=['GET'])
def containers_info(model):
    """
    查询所有容器相关信息
    :param model: 环境，prod or test
    :return:
    """
    current_app.logger.info("request base_url: %s" % request.base_url)
    current_app.logger.info("request url: %s" % request.url)
    current_app.logger.info("Selected environment: %s" % model)
    current_app.logger.info("Input parameter: %s" % request.args)

    if model == "prod":
        base_url_list = current_app.config.get('BASE_URL_PROD')
        local_file = current_app.config.get('PROD_INFO_CONF')
    elif model == "test":
        base_url_list = current_app.config.get('BASE_URL_TEST')
        local_file = current_app.config.get('TEST_INFO_CONF')
    else:
        return jsonify(res_404), 404

    # 如端点不可用，则加载本地数据返回
    for base_url in base_url_list:
        endpoint = base_url.split('/')[2]
        res_code = check_endpoint_status(endpoint=endpoint)
        if res_code != 200:
            try:
                fp = os.path.join(
                    current_app.config.get('BASE_DIR'),
                    'data',
                    local_file)
                obj = SerializeData(fp)
                res = obj.load_file()
                return jsonify(res)
            except Exception as e:
                current_app.logger.info(str(e))
                res_500['error'] = 'endpoint connect failed and load local file failed'
                return jsonify(res_500)

    # 判断是否传入cip参数
    if request.args.get('cip'):
        container_ip = request.args.get('cip')
        res_data = None
        flag = False
        for base_url in base_url_list:
            try:
                res_data = get_name_by_ip(
                    base_url=base_url,
                    request_url=request.base_url,
                    container_ip=container_ip)
            except Exception as e:
                current_app.logger.error(e)
                res_500['error'] = str(e)
                return jsonify(res_500), 500
            if res_data:
                flag = True
                break
        if flag:
            return jsonify(res_data)
        else:
            return jsonify(res_404), 404

    # 判断是否传入status参数
    filters = {}
    if request.args.get('status'):
        filters['status'] = request.args.get('status')

    container_data = {}
    for base_url in base_url_list:
        current_app.logger.info("base_url: %s" % base_url)
        try:
            docker_client = DockerEngine(base_url=base_url)
            container_data = dict_merge(
                container_data,
                docker_client.container_info(filters=filters))
        except Exception as e:
            current_app.logger.error(e)
            res_500['error'] = str(e)
            return jsonify(res_500), 500
    if container_data:
        return jsonify(container_data)
    else:
        current_app.logger.info("query result is empty")
        return jsonify(res_404), 404


@containers_bp.route('/info/<model>/<container_name>', methods=['GET'])
def container_info(model, container_name):
    """
    查询一个容器的相关信息
    :param model: 环境，prod or test
    :param container_name: 容器名称
    :return: 字典
    """
    current_app.logger.info("request base_url: %s" % request.base_url)
    current_app.logger.info("request url: %s" % request.url)
    container_data = {}
    current_app.logger.info("Selected environment: %s" % model)
    if model == "prod":
        base_url_list = current_app.config.get('BASE_URL_PROD')
    elif model == "test":
        base_url_list = current_app.config.get('BASE_URL_TEST')
    else:
        return jsonify(res_404), 404

    filters = {
        "name": container_name
    }

    for base_url in base_url_list:
        # 检查端点是否可连接
        endpoint = base_url.split('/')[2]
        res_code = check_endpoint_status(endpoint=endpoint)
        if res_code != 200:
            res_403["error"] = "%s endpoint connection failure !!!" % base_url
            return jsonify(res_403), 403
        # 处理请求
        current_app.logger.info("base_url: %s" % base_url)
        try:
            docker_client = DockerEngine(base_url=base_url)
            container_data = dict_merge(
                container_data,
                docker_client.container_info(filters=filters))
        except Exception as e:
            current_app.logger.error(e)
            res_500['error'] = str(e)
            return jsonify(res_500), 500
    if container_data:
        return jsonify(container_data)
    else:
        return jsonify(res_404), 404


@containers_bp.route('/stats/<model>/<container_ip>')
def container_stats(model, container_ip):
    """
    根据容器IP查询容器使用的资源
    :param model: 环境，prod or test
    :param container_ip: 容器IP
    :return: 字典
    """
    current_app.logger.info("request base_url: %s" % request.base_url)
    current_app.logger.info("request url: %s" % request.url)
    if model == "prod":
        base_url_list = current_app.config.get('BASE_URL_PROD')
    elif model == "test":
        base_url_list = current_app.config.get('BASE_URL_TEST')
    else:
        return jsonify(res_404), 404

    for base_url in base_url_list:
        # 检查端点是否可连接
        endpoint = base_url.split('/')[2]
        res_code = check_endpoint_status(endpoint=endpoint)
        if res_code != 200:
            res_403["error"] = "%s endpoint connection failure !!!" % base_url
            return res_403, 403
        # 处理请求
        current_app.logger.info("base_url: %s" % base_url)
        try:
            container_obj = get_name_by_ip(
                base_url=base_url,
                container_ip=container_ip)
        except Exception as e:
            current_app.logger.error(e)
            res_500['error'] = str(e)
            return jsonify(res_500), 500
        if container_obj:
            res_data = {}
            container_name = list(container_obj.keys())[0]
            current_app.logger.info("container_name: %s" % container_name)
            try:
                docker_client = DockerEngine(base_url=base_url)
            except Exception as e:
                current_app.logger.error(e)
                res_500['error'] = str(e)
                return jsonify(res_500), 500
            # 获取容器资源使用信息
            stats_dict = docker_client.container_stats(
                container_name=container_name)
            memory_usage = stats_dict['memory_stats']['usage'] - \
                stats_dict['memory_stats']['stats']['cache']
            memory_limit = stats_dict['memory_stats']['limit']
            memory_used_percent = "%.2f" % (memory_usage / memory_limit * 100)
            cpu_delta = stats_dict['cpu_stats']['cpu_usage']['total_usage'] - \
                stats_dict['precpu_stats']['cpu_usage']['total_usage']
            system_cpu_delta = stats_dict['cpu_stats']['system_cpu_usage'] - \
                stats_dict['precpu_stats']['system_cpu_usage']
            try:
                number_cpus = stats_dict['cpu_stats']['online_cpus']
            except Exception as e:
                current_app.logger.warning(e)
                number_cpus = len(
                    stats_dict['cpu_stats']['cpu_usage']['percpu_usage'])
            cpu_usage_precent = "%.2f" % (
                cpu_delta / system_cpu_delta * number_cpus * 100)
            # 返回对象赋值
            res_data['access_time'] = utc_to_beijing(stats_dict['read'])
            res_data['container_name'] = stats_dict['name'].replace('/', '')
            res_data['pids_stats'] = stats_dict['pids_stats']
            res_data['memory_stats'] = {}
            res_data['memory_stats']['memory_usage'] = byte_humanized_display(
                memory_usage)
            res_data['memory_stats']['memory_limit'] = byte_humanized_display(
                memory_limit)
            res_data['memory_stats']['memory_usage_precent'] = str(
                memory_used_percent) + "%"
            res_data['cpu_stats'] = {}
            res_data['cpu_stats']['cpu_limit'] = number_cpus
            res_data['cpu_stats']['cpu_usage_precent'] = str(
                cpu_usage_precent) + "%"
            return res_data
    else:
        return jsonify(res_404), 404


@containers_bp.route('/info/prod/single', methods=['GET'])
def containers_info_single():
    """
    查询生产环境中单路运行的容器
    :return: 字典
    """
    current_app.logger.info("request base_url: %s" % request.base_url)
    current_app.logger.info("request url: %s" % request.url)
    base_url_list = current_app.config.get('BASE_URL_PROD')

    # 如端点不可用，则加载本地数据返回
    for base_url in base_url_list:
        endpoint = base_url.split('/')[2]
        res_code = check_endpoint_status(endpoint=endpoint)
        if res_code != 200:
            try:
                fp = os.path.join(
                    current_app.config.get('BASE_DIR'),
                    'data',
                    current_app.config.get('PROD_INFO_SINGLE_CONF'))
                obj = SerializeData(fp)
                res = obj.load_file()
                return jsonify(res)
            except Exception as e:
                current_app.logger.info(str(e))
                res_500['error'] = 'endpoint connect failed and load local file failed'
                return jsonify(res_500)

    containers_dict = {}
    containers_key_list = []
    for endpoint_url in base_url_list:
        current_app.logger.info("endpoint_url: %s" % endpoint_url)
        matchObj = re.match(r'tcp://(.*):2375', endpoint_url, re.S)
        node_name = matchObj.group(1)
        try:
            docker_client = DockerEngine(base_url=endpoint_url)
            containers_dict[node_name] = docker_client.container_info()
            containers_key_list.append(
                set(docker_client.container_info().keys()))
        except Exception as e:
            current_app.logger.error(e)
            res_500['error'] = str(e)
            return jsonify(res_500), 500

    intersection_key = set.intersection(*containers_key_list)
    for node_key in list(containers_dict.keys()):
        for key in list(containers_dict[node_key].keys()):
            if key in intersection_key:
                containers_dict[node_key].pop(key)
    return jsonify(containers_dict)
